package com.aceitunaproject.server.player;

import java.io.Serializable;
import java.nio.ByteBuffer;

import com.aceitunaproject.server.ServerConfiguration;
import com.aceitunaproject.server.message.DefaultMessageHandler;
import com.aceitunaproject.server.message.MessageHandler;
import com.aceitunaproject.server.unit.UnitManager;
import com.aceitunaproject.server.unit.UnitManagerImpl;
import com.aceitunaproject.shared.message.Message;
import com.aceitunaproject.shared.message.MessageConstants;
import com.sun.sgs.app.AppContext;
import com.sun.sgs.app.ClientSession;
import com.sun.sgs.app.ClientSessionListener;
import com.sun.sgs.app.DataManager;
import com.sun.sgs.app.ManagedObject;
import com.sun.sgs.app.ManagedReference;


/**
 * Player object
 * 
 * @author Maciej Miechowicz
 */
public class Player implements ClientSessionListener, ManagedObject, Serializable {

	private static final long serialVersionUID = -865505294778439925L;

	private String name;
	private ManagedReference<ClientSession> sessionRef;
	private MessageHandler messageHandler;
	private ManagedReference<UnitManagerImpl> unitManagerRef;
		
	/**
	 * Creates a new player instance
	 * 
	 * @param name player name
	 * @throws NullPointerException if the name is null
	 */
	public Player(String name) {
		if(name == null)
			throw new NullPointerException("Null name");
		
		this.name = name;
		this.messageHandler = new DefaultMessageHandler();
		/*this.unitManagerRef = AppContext.getDataManager().
        createReference(new UnitManagerImpl(this));*/
		
		
	}
	
	/**
	 * Returns player name
	 * 
	 * @return player name
	 */
	public String getName() {
		return name;
	}

	/**
	 * Returns player message handler
	 * 
	 * @return player message handler
	 */
	public MessageHandler getMessageHandler() {
		return messageHandler;
	}
	
	/**
	 * Returns player session or <code>null</code> if session
	 * is not available
	 * 
	 * @return player session
	 */
	private ClientSession getSession() {
		return (sessionRef != null ? sessionRef.get() : null);
	}

	/**
	 * Sets player session
	 * 
	 * @param session player session to set
	 */
	public void setSession(ClientSession session) {
        DataManager dataManager = AppContext.getDataManager();

        // mark self for update so the session reference can be updated
        dataManager.markForUpdate(this);
        sessionRef = dataManager.createReference(session);
	}
	
	/**
	 * Handles player disconnect
	 */
	@Override
	public void disconnected(boolean graceful) {
		// TODO: implement
	}

	/**
	 * Indicates if the player is connected
	 * 
	 * @return connection indicator
	 */
	public boolean isConnected() {
		ClientSession session = getSession();
		return (session != null && session.isConnected());
	}

	/**
	 * Sends a binary message to the client
	 * 
	 * @param message binary message to send
	 * @throws NullPointerException if the message is null
	 */
	public void sendBinaryMessage(ByteBuffer message) {
		if(message == null)
			throw new NullPointerException("Null message");
		
		ByteBuffer msg = ByteBuffer.allocate(1 + message.remaining());
		msg.put(MessageConstants.TYPE_BINARY);
		msg.put(message);
		msg.flip();
		sendMessage(msg);
	}
	
	/**
	 * Sends an object to the client
	 * 
	 * @param message object to send
	 * @throws NullPointerException if the message is null
	 */
	public void sendObjectMessage(Message message) {
		if(message == null)
			throw new NullPointerException("Null message");
		
		byte[] serializedObject = ServerConfiguration.getObjectSerializer().serialize(message);
		
		ByteBuffer msg = ByteBuffer.allocate(1 + serializedObject.length);
		msg.put(MessageConstants.TYPE_OBJECT);
		msg.put(serializedObject);
		msg.flip();
		sendMessage(msg);
	}
	
	/**
	 * Sends a message to the client
	 * 
	 * @param message message to send
	 * @throws IllegalStateException if the player is not connected
	 */
	private void sendMessage(ByteBuffer message) {
		ClientSession session = getSession();
		if(session == null)
			throw new IllegalStateException("Not connected");
			
		session.send(message);
	}
	
	/**
	 * Handles client message receipt
	 */
	@Override
	public void receivedMessage(ByteBuffer message) {
		int messageType = message.get();
		if(messageType == MessageConstants.TYPE_BINARY) {
			messageHandler.handleBinaryMessage(this, message.asReadOnlyBuffer());
		} else {
			byte[] buffer = new byte[message.remaining()];
			message.get(buffer);

			Message deserializedMessage = 
				(Message)ServerConfiguration.getObjectSerializer().deserialize(buffer);
			messageHandler.handleObjectMessage(this, deserializedMessage);
		}
	}

}
